package com.atguigu.yuntai.web.controller.lineage;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yuntai.lineage.bean.EchartLink;
import com.atguigu.yuntai.lineage.bean.EchartNode;
import com.atguigu.yuntai.lineage.bean.Insert;
import com.atguigu.yuntai.lineage.bean.Node;
import com.atguigu.yuntai.lineage.service.LineageService;
import com.atguigu.yuntai.lineage.service.Neor4jService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * 血缘关系 前端控制器
 */
@RestController
@RequestMapping("/lineage")
@Api(tags = "血缘关系")
//@CrossOrigin //跨域
@Slf4j
public class LineageController {

    @Autowired
    LineageService lineageService;
    @Autowired
    Neor4jService neor4jService;

    /**
     *
     * @return
     */
    @ApiOperation(value = "更新血缘关系")
    @GetMapping("/refresh")
    @CrossOrigin
    public String refreshLineage(){
        // 遍历ds 的sql表
        lineageService.syncTaskSqlToNeo4j();

        return  "success";
    }

    /**
     * 根据表名查询血缘关系
     * @param tableName
     * @return
     */
    @ApiOperation(value = "根据类型查询治理信息(选项)")
    @GetMapping("/query/{tableName}")
    @CrossOrigin
    public String  query(
            @ApiParam(name = "tableName", value = "表名", required = true)
            @PathVariable String tableName){

        List<Insert> insertList = neor4jService.matchLineage(tableName);

        // 由于echarts不适合显示自循环结构，剪除，startnode与endnode相同的关系
        removeSelfNodeList(  insertList);

        Node centerNode = formatTableNode(insertList, tableName);

        List<EchartNode> echartNodeList = calNodesMap(centerNode);
        //当一个节点成为多个节点上游时  只保留最坐标的
        try {
            echartNodeList=   distinctNode(echartNodeList );
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        List<EchartLink> echartLinkList = genEchartLink(insertList);

        JSONObject jsonObject=new JSONObject();
        jsonObject.put("data",echartNodeList);
        jsonObject.put("link",echartLinkList);

        return jsonObject.toJSONString();
    }


//    public static void main(String[] args) {
////        ArrayList<EchartNode> echartNodes = new ArrayList<>();
////        echartNodes.add( EchartNode.builder().x(100).y(100).name("A").symbolSize(11).build());
////        echartNodes.add( EchartNode.builder().x(200).y(100).name("A").build());
////        echartNodes.add( EchartNode.builder().x(300).y(100).name("B").build());
////        echartNodes.add( EchartNode.builder().x(400).y(100).name("B").build());
////        distinctNode(echartNodes);
//
//    }

    private   List<EchartNode>    distinctNode(List<EchartNode> echartNodeList) throws InvocationTargetException, IllegalAccessException {
        List<EchartNode> distEchartNodeList = new LinkedList<>();
        for (EchartNode oldNode : echartNodeList) {

            boolean insertFlag=true;
            for (EchartNode newNode : distEchartNodeList) {
                if(oldNode.getName().equals(newNode.getName())){
                    insertFlag=false;
                    if(newNode.getItemStyle()!=null&& oldNode.getItemStyle()==null ){
                        BeanUtils.copyProperties(oldNode,newNode);

                    }else if (oldNode.getSymbolSize()==null&& newNode.getSymbolSize()==null){
                        if(newNode.getX()+newNode.getY()<oldNode.getY()+oldNode.getY() ){
                            BeanUtils.copyProperties(oldNode,newNode);
                        }

                    }
                }
            }
            if (insertFlag){
                distEchartNodeList.add(oldNode);
            }
        }
        return  distEchartNodeList;

    }

    private  List<EchartLink>  genEchartLink(List<Insert> insertList){
        List<EchartLink> echartLinkList = new ArrayList<>();
        for (Insert insert : insertList) {
            EchartLink echartLink = new EchartLink(insert.getStartNode().getName(), insert.getEndNode().getName());
            echartLinkList.add(echartLink);
        }
        return  echartLinkList;
    }

    private void   removeSelfNodeList(List<Insert> insertList){
        for (Iterator<Insert> iterator = insertList.iterator(); iterator.hasNext(); ) {
            Insert insert = iterator.next();
            if(insert.getStartNode().getName().equals(insert.getEndNode().getName())){
                iterator.remove();
            }
        }

    }



    private  Node formatTableNode(List<Insert> insertList,String tableName){


        Map<String,Node> nodeMap=new HashMap();
        Node centerNode =  null;

        for (Iterator<Insert> iterator = insertList.iterator(); iterator.hasNext(); ) {
            Insert insert = iterator.next();
            Node fromNode = nodeMap.get(insert.getStartNode().getName());
            Node targetNode = nodeMap.get(insert.getEndNode().getName());
            if(fromNode==null ){
                fromNode=new Node();
                fromNode.setTableName(insert.getStartNode().getName());
            }
            if(targetNode==null ){
                targetNode=new Node();
                targetNode.setTableName(insert.getEndNode().getName());
            }
            fromNode.addTargetNode(targetNode);
            targetNode.addFromNode(fromNode);
            nodeMap.put(insert.getStartNode().getName(),fromNode);
            nodeMap.put(insert.getEndNode().getName(),targetNode);

            if(fromNode.getTableName().equals(tableName)){
                centerNode=fromNode;
            }else if (targetNode.getTableName().equals(tableName)){
                centerNode=targetNode;
            }
        }

        return  centerNode;




    }


    public static final  int xMinWidth=100;
    public static final  int xAllWidth=600;
    public static final  int yMinHeight=50;
    public static final  int yAllHeight=400;
    public static final  int leftBlank=200;
    public static final  int highBlank=200;

    private  List<EchartNode>  calNodesMap(Node centerNode){
        List<EchartNode> echartNodeList= new ArrayList<>();

        Map<Integer,List<Node>> nodeLeftMap=new HashMap<>();
        Map<Integer,List<Node>> nodeRightMap=new HashMap<>();
        calLeftNode(nodeLeftMap, centerNode,  0);
        calRightNode( nodeRightMap, centerNode,0);

        //分x轴像素
        int widthLevel= nodeLeftMap.size()+nodeRightMap.size()-1;
        int xWidth = Math.max(xMinWidth , xAllWidth/widthLevel);

        int centerX = (nodeLeftMap.size() - 1) * xWidth + leftBlank;


        for (Map.Entry<Integer, List<Node>> integerListEntry : nodeLeftMap.entrySet()) {
            Integer level = integerListEntry.getKey();
            List<Node> nodeList = integerListEntry.getValue();


            int rowNo=0;
            int rowCount = nodeList.size();
            int yHeight = Math.max(yMinHeight , yAllHeight/rowCount);
            for (Node node : nodeList) {

                if(nodeList.size()==1){
                    EchartNode echartNode = EchartNode.builder()
                            .name(node.getTableName())
                            .x(centerX- level*xWidth)
                            .y(yAllHeight / 2 + highBlank).build();

                    if(level==0){//中间点
                        echartNode.setSymbolSize(100);
                        Map style=new HashMap();
                        style.put("color","red");
                        echartNode.setItemStyle(style);
                    }

                    echartNodeList.add(echartNode);
                }else{
                    EchartNode echartNode = EchartNode.builder()
                            .name(node.getTableName())
                            .x(centerX- level*xWidth)
                            .y(highBlank+  ((rowNo++) *yHeight)).build();
                    echartNodeList.add(echartNode);
                }

            }

        }
        for (Map.Entry<Integer, List<Node>> integerListEntry : nodeRightMap.entrySet()) {
            Integer level = integerListEntry.getKey();
            List<Node> nodeList = integerListEntry.getValue();

            int rowNo=0;
            int rowCount = nodeList.size();
            int yHeight = Math.max(yMinHeight , yAllHeight/rowCount);
            for (Node node : nodeList) {
                if(level!=0) {  //中间点 不重复

                    if (nodeList.size() == 1) {
                        EchartNode echartNode = EchartNode.builder()
                                .name(node.getTableName())
                                .x(centerX + level * xWidth)
                                .y(yAllHeight / 2 + highBlank).build();
                        echartNodeList.add(echartNode);
                    } else { //中间点 不重复放值
                        EchartNode echartNode = EchartNode.builder()
                                .name(node.getTableName())
                                .x(centerX + level * xWidth)
                                .y(highBlank + ((rowNo++) * yHeight)).build();
                        echartNodeList.add(echartNode);

                    }
                }
            }

        }

        return echartNodeList;
    }


    private void  calLeftNode(Map<Integer,List<Node>> nodeListMap, Node node,int level){

        List<Node> thisLevelNodeList = nodeListMap.get(level);
        if(thisLevelNodeList==null){
            thisLevelNodeList= new ArrayList<>()  ;
            nodeListMap.put(level,thisLevelNodeList);
        }
        thisLevelNodeList.add(node);
        if(node.getFromNodes()==null||node.getFromNodes().size()==0){
            return;
        }else{
            for (Node fromNode : node.getFromNodes()) {
                calLeftNode(nodeListMap,   fromNode,  level+1);
            }
        }

    }


    private void  calRightNode(Map<Integer,List<Node>> nodeListMap, Node node,int level){

        List<Node> thisLevelNodeList = nodeListMap.get(level);
        if(thisLevelNodeList==null){
            thisLevelNodeList= new ArrayList<>()  ;
            nodeListMap.put(level,thisLevelNodeList);
        }
        thisLevelNodeList.add(node);
        if(node.getTargetNodes()==null||node.getTargetNodes().size()==0){
            return;
        }else{
            for (Node targetNode : node.getTargetNodes()) {
                calRightNode(nodeListMap,   targetNode,  level+1);
            }
        }

    }

}
